#ifndef __ARR2D_OPERATORS_HPP__
#define __ARR2D_OPERATORS_HPP__

#include <vector>
#include <stdexcept>

template <class T>
class Arr2d 
{
public:

  /// This is the default constructor. It doesn't do anything:
  Arr2d() :
    nrows(0), ncols(0)
  {
  }

  /// Create a 2D array that is nr by nc.
  Arr2d(int nr, int nc)
  {
    resize(nr, nc);
  }

  /// Create a 2D array that is given a default value:
  Arr2d(int nr, int nc, T val)
  {
    resize(nr, nc);
    setall(val);
  }

  /// Resize the array:
  void resize(int nr, int nc)
  {
    // Make sure that the number of rows is non-negative:
    if(nr < 0) {
      throw(std::runtime_error("Number of rows cannot be negative"));
    }

    if(nc < 0) {
      throw(std::runtime_error("Number of columns cannot be negative"));
    }

    nrows = nr;
    ncols = nc;

    storage.resize(nrows);
    for(int i = 0; i < nrows; i++) {
      storage[i].resize(ncols);
    }    
  }

  /// Set all of the array elements to some value:
  void setall(T val)
  {
    int i, j;
    for(i = 0; i < nrows; i++) {
      for(j = 0; j < ncols; j++) {
	storage[i][j] = val;
      }
    }
  }

  /// *****************
  /// ***           ***
  /// *** OPERATORS ***
  /// ***           ***
  /// *****************

  /// We've replaced our get functions with overloaded () operators:


  /// Get an element of the array. By returning a reference, we can
  /// use this function to change the value:
  T& operator()(int r, int c)
  {
    return storage[r][c];
  }

  /// Get an elemnt of the array. This can be used for const arrays
  const T& operator()(int r, int c) const
  {
    return storage[r][c];
  }  

private:

  /// The storage member stores all of our data:
  std::vector<std::vector<T> > storage;

  /// The number of rows:
  int nrows;

  /// The number of columns:
  int ncols;

};  // <-- Make sure that you include this semicolon


/// Overload the insertion stream operator so we can easily print the
/// array. Note that this function is not implemented as a member
/// function.
template <class T>
std::ostream& operator<<(std::ostream& os, const Arr2d<T>& arr2d)
{
  int i, j;
  
  // Print the array:
  for(i = 0; i < 5; i++){
    for(j = 0; j < 4; j++) {
      os << arr2d(i,j) << '\t';
    }
    os << std::endl;
  }
  
  return os;
}

#endif
